home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
challenge
/
12.04-Apr96
/
LifeTestCode DR3.sit
/
Life TestCode DR3
/
Chal9604TC.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-04-03
|
38KB
|
1,555 lines
/* MacTech Challenge April 1996
MutantLife Test Code
©Ludovic Nicolle 1996
Link your solution in PropagateLife.c
and include Chal9604TC.rsrc in the project.
Data files available to test with can be obtained from the
challenge ftp site, and on a limited basis from the Challenge-D
Mailing List
/*
constants
*/
#define kAppleMenu 128
#define kFileMenu 129
#define kLifeMenu 131
#define kTestMenu 132
#define kNew 1
#define kOpen 2
#define kClose 3
#define kSave 5
#define kSaveAs 6
#define kAppend 7
#define kQuit 12
#define kChangeSettings 1
#define kPropagate 3
#define kPropagateOneStep 4
#define kReturnToInitial 6
#define kStartFromCurrent 7
#define kTestWhole 1
#define kTestContinue 2
#define kDisplayInitial 4
#define kDisplayPrevious 5
#define kDisplayCurrent 6
#define kDisplayGood 8
#define kDisplayErrors 9
/* custom compiler flags*/
/*profiling under CodeWarrior */
#ifdef __MWERKS__
//#define _profile_ /*Uncomment this if you want to profile under CW */
#include "profiler.h"
#endif
#include "stdio.h"
/* Solutions header */
#include "PropagateLife.h"
/* Bitmap Initialization function */
#include "LifeScramble.h"
/* File format, Saving and Opening */
#include "LifeFile.h"
/* Graphic Display of the Bitmaps in a window */
#include "LifeWindow.h"
short gQuit = false;
/* timing utils. See example of use in PropagateCurrent()*/
#include <Timer.h>
UnsignedWide mbefore,
mafter;
long mdiff = 0;
/*If you link some bitmap initialization routine or use the default one
and want it to be used on any new BitMap before manual setting, let this
value to 1. If you want to bypass automatic Initialization, set it to 0*/
#define _UseInitCells_ 0
/* Various Globals. File, Window, Bitmap etc. */
/* Don't change these without reflexion! */
WindowPtr gLifeW = nil;
short gLifeFile = 0;
FSSpec gLifeSpec;
Boolean gInitialSaved = false,
gCurrentSaved;
GrafPort gInitialState, /* allocate these four GrafPort statically*/
gPreviousState,
gCurrentState,
gTempFinalState,
gErrorState;
short gDisplayState = 0;
Rect gDisplayRect;
Rect defaultRect;
Boolean gInTest = false;
LifeRun gLifeRun;
long gCurGen = 0; /* current generation of current run. 0 = start */
Boolean gRecord = true;
Boolean gIntermediate = false;
short gInterStep = 1;
unsigned char gMessage1[256],
gMessage2[256];
/* End of don't-touch-if-you-don't-understand-gloabals */
/* DEFAULT APPEARANCE SETTINGS */
/* these controls are not critical and only change display
so change them and see */
Point gMessageZone = {50, 5};
Boolean gToroidLike = true;
short gToroidLimit = 3; /*under that size, dont draw grid and mirror*/
Boolean gDrawGrid = true;
/* DEFAULT LIFE SETTINGS */
/* these ones are available under the Chnage Settings Dialog.
you can chnage the default values here since */
long gMaxGens = 10;
Point gLifeSize = {20, 20};
short gBirth = 0x0008, /* Default birth and death rules */
gDeath = 0x01F3;
void InitAll(void);
void ChangeSettings(void);
void OpenFile(void);
void CloseFile(void);
void DoSave(void);
void AppendtoFile(void);
void NewRun(void);
void PropagateCurrent();
void PropagateCurrentbyOne();
void ReturnToInitial(void);
void StartFromCurrent(void);
void TestWholeFile(void);
void TestFileStep(void);
void UpdateLifeWindow(void);
void DoNothing();
void DoAboutBox(void);
void main(void);
void PrepareMenus(void);
void DoMenu(long menusel);
void DoKey(EventRecord *event);
void DoMouseDown(EventRecord *event);
void InitAll(void)
{
Handle mbarHdl;
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
//Get the Menu Bar
mbarHdl = GetNewMBar(128);
SetMenuBar(mbarHdl);
DisposeHandle(mbarHdl);
DrawMenuBar();
AppendResMenu(GetMenuHandle(kAppleMenu), 'DRVR');
OpenPort(&gInitialState);
OpenPort(&gPreviousState);
OpenPort(&gCurrentState);
OpenPort(&gTempFinalState);
OpenPort(&gErrorState);
gInitialState.portBits.baseAddr = nil;
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
gTempFinalState.portBits.baseAddr = nil;
gErrorState.portBits.baseAddr = nil;
{GDHandle gd;
short mBarHeight;
mBarHeight = LMGetMBarHeight();
gd = GetMainDevice();
defaultRect = (**gd).gdRect;
defaultRect.top = mBarHeight+20;
defaultRect.left = 10;
defaultRect.right -= 10;
defaultRect.bottom -= 10;
}
}
void ChangeSettings(void)
{
GrafPtr savePort;
DialogPtr myDialog;
short itemHit = 0;
Str255 theString;
long stringvalue;
short iType;
Handle iHandle;
Rect iRect;
Point previousSize = gLifeSize;
short prevBirth = gBirth,
prevDeath = gDeath;
long prevMax = gMaxGens;
myDialog = GetNewDialog(128, nil, (WindowPtr)-1);
if (myDialog == nil)
return;
GetPort(&savePort);
SetPort(myDialog);
SetDialogDefaultItem(myDialog, 1);
SetDialogCancelItem(myDialog, 2);
GetDialogItem(myDialog, 3, &iType, &iHandle, &iRect);
NumToString(gMaxGens, theString);
SetDialogItemText(iHandle, theString);
GetDialogItem(myDialog, 4, &iType, &iHandle, &iRect);
NumToString(gLifeSize.h, theString);
SetDialogItemText(iHandle, theString);
GetDialogItem(myDialog, 5, &iType, &iHandle, &iRect);
NumToString(gLifeSize.v, theString);
SetDialogItemText(iHandle, theString);
GetDialogItem(myDialog, 39, &iType, &iHandle, &iRect);
NumToString(gInterStep, theString);
SetDialogItemText(iHandle, theString);
GetDialogItem(myDialog, 9, &iType, &iHandle, &iRect);
SetControlValue((ControlHandle)iHandle, gRecord);
GetDialogItem(myDialog, 10, &iType, &iHandle, &iRect);
SetControlValue((ControlHandle)iHandle, gIntermediate);
for (itemHit = 0; itemHit <= 8; itemHit++)
{
GetDialogItem(myDialog, 11 + itemHit * 2, &iType, &iHandle, &iRect);
SetControlValue((ControlHandle)iHandle, (gBirth >> itemHit) & 0x01);
}
for (itemHit = 0; itemHit <= 8; itemHit++)
{
GetDialogItem(myDialog, 12 + itemHit * 2, &iType, &iHandle, &iRect);
SetControlValue((ControlHandle)iHandle, (gDeath >> itemHit) & 0x01);
}
ShowWindow(myDialog);
while ((itemHit != 1) && (itemHit != 2))
{
ModalDialog(nil, &itemHit);
if ((itemHit >= 9) && (itemHit <= 28)) /*checkbox inversion*/
{
GetDialogItem(myDialog, itemHit, &iType, &iHandle, &iRect);
iType = GetControlValue((ControlHandle)iHandle);
SetControlValue((ControlHandle)iHandle, !iType);
}
}
if (itemHit == 2)
{
DisposeDialog(myDialog);
SetPort(savePort);
return;
}
GetDialogItem(myDialog, 3, &iType, &iHandle, &iRect);
GetDialogItemText(iHandle, theString);
StringToNum(theString, &gMaxGens);
gMaxGens = gMaxGens & 0x0000FFFF;/*limit to 64k generations*/
if (gMaxGens == 0)
gMaxGens =1;
GetDialogItem(myDialog, 4, &iType, &iHandle, &iRect);
GetDialogItemText(iHandle, theString);
StringToNum(theString, &stringvalue);
gLifeSize.h = stringvalue & 0x000003FF;
GetDialogItem(myDialog, 5, &iType, &iHandle, &iRect);
GetDialogItemText(iHandle, theString);
StringToNum(theString, &stringvalue);
gLifeSize.v = stringvalue & 0x000001FF;
if (gLifeSize.h < 3)
gLifeSize.h = 3;
if ( gLifeSize.v < 3)
gLifeSize.v = 3;
GetDialogItem(myDialog, 39, &iType, &iHandle, &iRect);
GetDialogItemText(iHandle, theString);
StringToNum(theString, &stringvalue);
gInterStep = stringvalue & 0x00000FFF;
GetDialogItem(myDialog, 9, &iType, &iHandle, &iRect);
gRecord = GetControlValue((ControlHandle)iHandle);
GetDialogItem(myDialog, 10, &iType, &iHandle, &iRect);
gIntermediate = GetControlValue((ControlHandle)iHandle);
gBirth = 0;
for (itemHit = 0; itemHit <= 8; itemHit++)
{
GetDialogItem(myDialog, 11 + itemHit * 2, &iType, &iHandle, &iRect);
gBirth += GetControlValue((ControlHandle)iHandle) << itemHit;
}
gDeath = 0;
for (itemHit = 0; itemHit <= 8; itemHit++)
{
GetDialogItem(myDialog, 12 + itemHit * 2, &iType, &iHandle, &iRect);
gDeath += GetControlValue((ControlHandle)iHandle) << itemHit;
}
SetPort(savePort);
DisposeDialog(myDialog);
if ((previousSize.h != gLifeSize.h) || (previousSize.v != gLifeSize.v) ||
(prevBirth != gBirth) || (prevDeath != gDeath) || (prevMax != gMaxGens))
{
if (gInitialState.portBits.baseAddr != nil)
DisposePtr(gInitialState.portBits.baseAddr);
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
if (gCurrentState.portBits.baseAddr != nil)
DisposePtr(gCurrentState.portBits.baseAddr);
gInitialState.portBits.baseAddr = nil;
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
if (gLifeW != nil)
{
GetPort(&savePort);
SetPort(gLifeW);
EraseRect(&((GrafPtr)gLifeW)->portRect);
SetPort(savePort);
}
}
}
void OpenFile(void)
{
OSErr iErr;
GrafPtr savePort;
long fileRuns;
BitMapStep startCells;
if (gLifeFile != 0)
CloseLifeFile(&gLifeFile);
CloseFile();
iErr = OpenLifeFile(&gLifeFile, &gLifeSpec);
if (iErr)
return;
if (gLifeW == nil)
{
gLifeW = NewWindow(nil, &defaultRect, gLifeSpec.name, false, documentProc,
(WindowPtr) -1, true, 0);
if (gLifeW == nil)
{
gQuit = true; /*big trouble, so quit */
return;
}
}
SetWTitle(gLifeW, gLifeSpec.name);
if (gInitialState.portBits.baseAddr == nil)
{
iErr = GetRunNumber(gLifeFile, &fileRuns);
if (iErr || (fileRuns <= 0))
return;
iErr = ReadLifeRun(gLifeFile,&gLifeRun, 1);
gMaxGens = gLifeRun.maxGen;
gBirth = gLifeRun.birthRules;
gDeath = gLifeRun.deathRules;
SetPt(&gLifeSize, gLifeRun.worldSize.right - gLifeRun.worldSize.left,
gLifeRun.worldSize.bottom - gLifeRun.worldSize.top);
iErr = ReadLifeWorld(gLifeFile, &startCells, 1, 1);
gInitialState.portBits = startCells.cellsBM;
gDisplayState = 1;
/* Bob's code */
{Point hvRatio;
short ratio;
gDisplayRect = defaultRect;
gDisplayRect.left += gMessageZone.h + 1;
gDisplayRect.top += gMessageZone.v + 1;
ratio = GiveRatio(&gInitialState, &gDisplayRect, &hvRatio);
if (ratio == 0)
{
SizeWindow(gLifeW,defaultRect.right - defaultRect.left,
defaultRect.bottom - defaultRect.top, true);
} else
{
SizeWindow(gLifeW,ratio*(gLifeSize.h + 2) + gMessageZone.h + 1,
ratio*(gLifeSize.v + 2) + gMessageZone.v + 1, true);
}
ShowWindow(gLifeW);
}
if (iErr)
return;
GetPort(&savePort);
SetPort(&gInitialState);
SetPortBits(&startCells.cellsBM);
gDisplayRect = ((GrafPtr)gLifeW)->portRect;
gDisplayRect.top += gMessageZone.v + 1;
gDisplayRect.left += gMessageZone.h + 1;
// DisplayCells(&gInitialState, gLifeW, &gDisplayRect);
gInitialSaved = gCurrentSaved = true;
gCurGen = 0;
SetPort(savePort);
}
}
void CloseFile(void)
{
BitMapStep newStep;
LifeRun newRun;
OSErr iErr;
DialogPtr myDialog;
short itemHit;
if (gRecord && (gLifeFile == 0) && (gInitialState.portBits.baseAddr != nil))
{ /* propose the user to create a file */
myDialog = GetNewDialog(129, nil, (WindowPtr)-1);
if (myDialog == nil)
return;
SetDialogDefaultItem(myDialog, 1);
SetDialogCancelItem(myDialog, 3);
ModalDialog(nil, &itemHit);
DisposeDialog(myDialog);
switch (itemHit)
{
case 1:
iErr = CreateLifeFile(&gLifeFile, &gLifeSpec);
break;
case 2:
iErr = OpenLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 3:
gRecord = false;
break;
default:
break;
}
}
if ((!gInitialSaved || !gCurrentSaved) && (gLifeFile != 0) && gRecord)
{ /* propose the user to create a file */
if ((!gInitialSaved) && (gInitialState.portBits.baseAddr != nil))
{
newRun.maxGen = gMaxGens;
newRun.birthRules = gBirth;
newRun.deathRules = gDeath;
newRun.filler1 = 0;
newRun.computedGen = 0;
newRun.rbytes = gInitialState.portBits.rowBytes;
newRun.worldSize = gInitialState.portBits.bounds;
newRun.steps = 0;
iErr = AppendLifeRun(gLifeFile, &newRun);
newStep.stepvalue = 0; /* 0 = initial state */
newStep.cellsBM = gInitialState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
gInitialSaved = true;
}
if ((!gCurrentSaved) && (gCurrentState.portBits.baseAddr != nil))
{
newStep.stepvalue = gCurGen; /* 0 = initial state */
newStep.cellsBM = gCurrentState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
gCurrentSaved = true;
}
}
if (gInitialState.portBits.baseAddr != nil)
DisposePtr(gInitialState.portBits.baseAddr);
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
if (gCurrentState.portBits.baseAddr != nil)
DisposePtr(gCurrentState.portBits.baseAddr);
gInitialState.portBits.baseAddr = nil;
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
if (gLifeW != nil)
DisposeWindow(gLifeW);
gLifeW = nil;
CloseLifeFile(&gLifeFile);
gDisplayState = 0;
gInTest = false;
}
void DoSave(void)
{
OSErr iErr;
BitMapStep newStep;
LifeRun newRun;
if (gInitialState.portBits.baseAddr == nil)
return;
if (gCurrentSaved && gInitialSaved && (gLifeFile != 0))
return;
if (gLifeFile == 0)
{
sprintf((char*)gLifeSpec.name, (char*)"\pUntitled");
iErr = CreateLifeFile(&gLifeFile, &gLifeSpec);
if (iErr)
return;
}
if (gLifeW) /*is that necessary??? */
SetWTitle(gLifeW, gLifeSpec.name);
if ((!gInitialSaved) && (gInitialState.portBits.baseAddr != nil))
{
newRun.maxGen = gMaxGens;
newRun.birthRules = gBirth;
newRun.deathRules = gDeath;
newRun.filler1 = 0;
newRun.computedGen = 0;
newRun.rbytes = gInitialState.portBits.rowBytes;
newRun.worldSize = gInitialState.portBits.bounds;
newRun.steps = 0;
iErr = AppendLifeRun(gLifeFile, &newRun);
newStep.stepvalue = 0; /* 0 = initial state */
newStep.cellsBM = gInitialState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
gInitialSaved = true;
}
if ((!gCurrentSaved) && (gCurrentState.portBits.baseAddr != nil))
{
newStep.stepvalue = gCurGen; /* 0 = initial state */
newStep.cellsBM = gCurrentState.portBits;
iErr = AppendLifeWorld(gLifeFile, &newStep);
if (!iErr)
gCurrentSaved = true;
}
}
void AppendtoFile(void)
{
if (gInitialState.portBits.baseAddr == nil)
return;
if (gLifeFile != 0)
CloseLifeFile(&gLifeFile);
if (!OpenLifeFile(&gLifeFile, &gLifeSpec))
{
gInitialSaved = gCurrentSaved = false;
DoSave();
}
}
void PropagateCurrent()
{
DialogPtr myDialog;
OSErr iErr;
short itemHit;
LifeRun newRun;
BitMapStep newStep;
long numRuns;
Rect messageRect;
if ((gInitialState.portBits.baseAddr == nil) || (gLifeW == nil))
NewRun();
if ((gInitialState.portBits.baseAddr == nil) || (gLifeW == nil))
return;
/*Make a copy of the cells */
gCurrentState.portBits = gInitialState.portBits;
gCurrentState.portBits.baseAddr = NewPtr(gCurrentState.portBits.rowBytes *
gLifeSize.v);
BlockMoveData(gInitialState.portBits.baseAddr,
gCurrentState.portBits.baseAddr, GetPtrSize(gCurrentState.portBits.baseAddr));
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
gPreviousState.portBits.baseAddr = nil;
/* Call User solution on current bitmap */
Microseconds(&mbefore);
gCurGen = PropagateLife(gCurrentState.portBits, gMaxGens, gBirth, gDeath);
Microseconds(&mafter);
mdiff = mafter.lo - mbefore.lo;
gCurrentSaved = false;
gDisplayRect = ((GrafPtr)gLifeW)->portRect;
gDisplayRect.top += gMessageZone.v + 1;
gDisplayRect.left += gMessageZone.h + 1;
DisplayCells(&gCurrentState, gLifeW, &gDisplayRect);
gDisplayState = 2;
sprintf((char*)gMessage1,
(char*)"\p Your routine computed %ld generations.", gCurGen);
sprintf((char*)gMessage2, (char*)"\p");
/*invalidate whole window to force solution display */
SetPort(gLifeW);
messageRect = ((GrafPtr)gLifeW)->portRect;
messageRect.bottom = gMessageZone.v;
InvalRect(&messageRect);
/*the two rects always overlap now */
/* saving stuff */
sprintf((char*)gLifeSpec.name, (char*)"\pUntitled");
if (gRecord && (gLifeFile == 0))
{ /* propose the user to create a file */
myDialog = GetNewDialog(129, nil, (WindowPtr)-1);
if (myDialog == nil)
return;
SetDialogDefaultItem(myDialog, 1);
SetDialogCancelItem(myDialog, 3);
ModalDialog(nil, &itemHit);
DisposeDialog(myDialog);
switch (itemHit)
{
case 1:
iErr = CreateLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 2:
iErr = OpenLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 3:
gRecord = false;
break;
default:
break;
}
}
if (gRecord)
{
if ((!gInitialSaved) && (gInitialState.portBits.baseAddr != nil))
{
newRun.maxGen = gMaxGens;
newRun.birthRules = gBirth;
newRun.deathRules = gDeath;
newRun.filler1 = 0;
newRun.computedGen = 0;
newRun.rbytes = gInitialState.portBits.rowBytes;
newRun.worldSize = gInitialState.portBits.bounds;
newRun.steps = 0;
if (AppendLifeRun(gLifeFile, &newRun))
return;
newStep.stepvalue = 0; /* 0 = initial state */
newStep.cellsBM = gInitialState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
gInitialSaved = true;
}
if ((!gCurrentSaved) && (gCurrentState.portBits.baseAddr != nil))
{
newStep.stepvalue = gCurGen; /* 0 = initial state */
newStep.cellsBM = gCurrentState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
{
if (GetRunNumber(gLifeFile, &numRuns))
return;
SetComputedGen(gLifeFile, numRuns, gCurGen);
gCurrentSaved = true;
}
}
}
}
void NewRun(void)
{
// Rect wRect;
GrafPtr savePort;
short width, rbytes;
OSErr iErr;
DialogPtr myDialog;
short itemHit;
LifeRun newRun;
BitMapStep newStep;
/* If gRecord is set and no file is open, ask user what to do */
sprintf((char*)gLifeSpec.name, (char*)"\pUntitled");
if (gRecord && (gLifeFile == 0))
{ /* propose the user to create a file */
myDialog = GetNewDialog(129, nil, (WindowPtr)-1);
if (myDialog == nil)
return;
SetDialogDefaultItem(myDialog, 1);
SetDialogCancelItem(myDialog, 3);
ModalDialog(nil, &itemHit);
DisposeDialog(myDialog);
switch (itemHit)
{
case 1:
iErr = CreateLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 2:
iErr = OpenLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 3:
gRecord = false;
break;
default:
break;
}
}
gInTest = false; /* for some cases, since the code is not well segmented*/
// Create the window if necessary
// wRect = defaultRect;
if (gLifeW == nil)
{
gLifeW = NewWindow(nil, &defaultRect, gLifeSpec.name, false, documentProc,
(WindowPtr) -1, true, 0);
if (gLifeW == nil)
{
gQuit = true;
return;
}
}
if (gInitialState.portBits.baseAddr != nil)
DisposePtr(gInitialState.portBits.baseAddr);
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
if (gCurrentState.portBits.baseAddr != nil)
DisposePtr(gCurrentState.portBits.baseAddr);
gInitialState.portBits.baseAddr = nil;
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
width = gLifeSize.h;
if (width % 32 > 0)
width += (32 - (width) % 32); /* should be 32 bits flush */
rbytes = width / 8;
gInitialState.portBits.rowBytes = rbytes;
SetRect(&gInitialState.portBits.bounds, 0, 0, gLifeSize.h, gLifeSize.v);
gInitialState.portBits.baseAddr = NewPtrClear(rbytes * gLifeSize.v);
if (gInitialState.portBits.baseAddr == nil)
return; /*error handling maybe ? */
gDisplayState = 1;
/* resize window to match problem size */
{Point hvRatio;
short ratio;
gDisplayRect = defaultRect;
gDisplayRect.left += gMessageZone.h + 1;
gDisplayRect.top += gMessageZone.v + 1;
ratio = GiveRatio(&gInitialState, &gDisplayRect, &hvRatio);
if (ratio == 0)
{
SizeWindow(gLifeW,defaultRect.right - defaultRect.left,
defaultRect.bottom - defaultRect.top, true);
} else
{
SizeWindow(gLifeW,ratio*(gLifeSize.h + 2) + gMessageZone.h + 1,
ratio*(gLifeSize.v + 2) + gMessageZone.v + 1, true);
}
ShowWindow(gLifeW);
}
GetPort(&savePort);
SetPort(&gInitialState);
#if _UseInitCells_
InitCellsPort(&gInitialState);
#endif
SetPort(gLifeW);
gDisplayRect = ((GrafPtr)gLifeW)->portRect;
gDisplayRect.top += gMessageZone.v + 1;
gDisplayRect.left += gMessageZone.h + 1;
EraseRect(&((GrafPtr)gLifeW)->portRect); /* erase old initial rect */
InvalRect(&((GrafPtr)gLifeW)->portRect);
gDisplayState = 1;
// DisplayCells(&gInitialState, gLifeW, &gDisplayRect);
gCurGen = 0;
gInitialSaved = false;
gCurrentSaved = true; /*doesn't exist yet */
/* Save to file if necessary */
if (gRecord)
{
newRun.maxGen = gMaxGens;
newRun.birthRules = gBirth;
newRun.deathRules = gDeath;
newRun.filler1 = 0;
newRun.computedGen = 0;
newRun.rbytes = rbytes;
newRun.worldSize = gInitialState.portBits.bounds;
newRun.steps = 0;
iErr = AppendLifeRun(gLifeFile, &newRun);
/* some error handling is missing here */
newStep.stepvalue = 0; /* 0 = initial state */
newStep.cellsBM = gInitialState.portBits;
iErr = AppendLifeWorld(gLifeFile, &newStep);
gInitialSaved = gCurrentSaved = true;
}
SetPort(savePort);
}
void PropagateCurrentbyOne()
{
DialogPtr myDialog;
OSErr iErr;
short itemHit;
LifeRun newRun;
BitMapStep newStep;
if ((gInitialState.portBits.baseAddr == nil) || (gLifeW == nil))
NewRun();
if ((gInitialState.portBits.baseAddr == nil) || (gLifeW == nil))
return;
gDisplayState = 1;
/*Make a copy of the cells */
if (gCurrentState.portBits.baseAddr == nil)
{
gCurrentState.portBits = gInitialState.portBits;
gCurrentState.portBits.baseAddr = NewPtr(gCurrentState.portBits.rowBytes *
gLifeSize.v);
if (gCurrentState.portBits.baseAddr == nil)
return;
BlockMoveData(gInitialState.portBits.baseAddr,
gCurrentState.portBits.baseAddr, GetPtrSize(gCurrentState.portBits.baseAddr));
}
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
gPreviousState.portBits = gCurrentState.portBits;
gPreviousState.portBits.baseAddr =
NewPtr(gCurrentState.portBits.rowBytes * gLifeSize.v);
if (gPreviousState.portBits.baseAddr == nil)
return;
BlockMoveData(gCurrentState.portBits.baseAddr,
gPreviousState.portBits.baseAddr,
GetPtrSize(gPreviousState.portBits.baseAddr));
/* Call User solution on current bitmap with one generation maximum*/
Microseconds(&mbefore);
gCurGen = PropagateLife(gCurrentState.portBits, 1, gBirth, gDeath);
Microseconds(&mafter);
mdiff = mafter.lo - mbefore.lo;
gCurrentSaved = false;
gDisplayState = 2;
gDisplayRect = ((GrafPtr)gLifeW)->portRect;
gDisplayRect.top += gMessageZone.v + 1;
gDisplayRect.left += gMessageZone.h + 1;
DisplayCells(&gCurrentState, gLifeW, &gDisplayRect);
/*the two rects always overlap in this version */
/* saving stuff */
sprintf((char*)gLifeSpec.name, (char*)"\pUntitled");
if (gRecord && (gLifeFile == 0))
{ /* propose the user to create a file */
myDialog = GetNewDialog(129, nil, (WindowPtr)-1);
if (myDialog == nil)
return;
SetDialogDefaultItem(myDialog, 1);
SetDialogCancelItem(myDialog, 3);
ModalDialog(nil, &itemHit);
DisposeDialog(myDialog);
switch (itemHit)
{
case 1:
iErr = CreateLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 2:
iErr = OpenLifeFile(&gLifeFile, &gLifeSpec);
if ((iErr) || (gLifeFile == 0))
return;
break;
case 3:
gRecord = false;
break;
default:
break;
}
}
if (gRecord)
{
if ((!gInitialSaved) && (gInitialState.portBits.baseAddr != nil))
{
newRun.maxGen = gMaxGens;
newRun.birthRules = gBirth;
newRun.deathRules = gDeath;
newRun.filler1 = 0;
newRun.computedGen = 0;
newRun.rbytes = gInitialState.portBits.rowBytes;
newRun.worldSize = gInitialState.portBits.bounds;
newRun.steps = 0;
if (AppendLifeRun(gLifeFile, &newRun))
return;
newStep.stepvalue = 0; /* 0 = initial state */
newStep.cellsBM = gInitialState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
gInitialSaved = true;
}
if ((!gCurrentSaved) && (gCurrentState.portBits.baseAddr != nil))
{
newStep.stepvalue = gCurGen; /* 0 = initial state */
newStep.cellsBM = gCurrentState.portBits;
if (!AppendLifeWorld(gLifeFile, &newStep))
gCurrentSaved = true;
}
}
}
void ReturnToInitial(void)
{
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
if (gCurrentState.portBits.baseAddr != nil)
DisposePtr(gCurrentState.portBits.baseAddr);
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
gDisplayState = 1;
gInTest = false; /* for some cases, since the code is not well segmented*/
DisplayCells(&gInitialState, gLifeW, &gDisplayRect);
}
void StartFromCurrent(void)
{
/*Destroy actual initial data*/
if (gInitialState.portBits.baseAddr != nil)
DisposePtr(gInitialState.portBits.baseAddr);
/*then set initial state to actual final state */
if (gCurrentState.portBits.baseAddr != nil)
gInitialState.portBits = gCurrentState.portBits;
/*Destroy Any Previous state if necessary */
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
gInitialSaved = gCurrentSaved = false;
gDisplayState = 1;
gInTest = false; /* for some cases, since the code is not well segmented*/
}
/* Globals flags for testing */
//Boolean gInTest
//Boolean testHasFile = false;
short testCurRun;
Boolean testReady;
Boolean testSolved;
long runNumber;
void TestWholeFile(void)
{
OSErr iErr;
long bytesCount, *checkPtr;
Rect messageRect;
Boolean good;
BitMapStep myBMStep;
GrafPtr savePort;
if (!gInTest){
if (gLifeW != nil)
DisposeWindow(gLifeW);
gLifeW = nil;
CloseLifeFile(&gLifeFile);
gDisplayState = 0;
OpenFile();
if (gLifeFile == 0)
return;
gInTest = true;
iErr = GetRunNumber(gLifeFile, &runNumber);
if (runNumber < 1)
{
gInTest = false;
return;
}
testCurRun = 1;
testReady = false;
testSolved = false;
}
if (testCurRun > runNumber)
{
CloseFile();
sprintf((char*)gMessage1, (char*)"\p");
sprintf((char*)gMessage2, (char*)"\p");
gInTest = false;
return;
}
GetPort(&savePort);
if (!testReady)
{
if (gInitialState.portBits.baseAddr != nil)
DisposePtr(gInitialState.portBits.baseAddr);
if (gPreviousState.portBits.baseAddr != nil)
DisposePtr(gPreviousState.portBits.baseAddr);
if (gCurrentState.portBits.baseAddr != nil)
DisposePtr(gCurrentState.portBits.baseAddr);
if (gTempFinalState.portBits.baseAddr != nil)
DisposePtr(gTempFinalState.portBits.baseAddr);
if (gErrorState.portBits.baseAddr != nil)
DisposePtr(gErrorState.portBits.baseAddr);
gInitialState.portBits.baseAddr = nil;
gPreviousState.portBits.baseAddr = nil;
gCurrentState.portBits.baseAddr = nil;
gTempFinalState.portBits.baseAddr = nil;
gErrorState.portBits.baseAddr = nil;
if (ReadLifeRun(gLifeFile, &gLifeRun, testCurRun))
{
gDisplayState = 0;
gInTest = false;
SetPort(savePort);
return;
}
if (gLifeRun.steps >= 1)
{
gBirth = gLifeRun.birthRules;
gDeath = gLifeRun.deathRules;
gMaxGens = gLifeRun.maxGen;
SetPt(&gLifeSize, gLifeRun.worldSize.right - gLifeRun.worldSize.left,
gLifeRun.worldSize.bottom - gLifeRun.worldSize.top);
iErr = ReadLifeWorld(gLifeFile, &myBMStep, testCurRun, 1);
if (myBMStep.stepvalue != 0)
{
testCurRun++;
SetPort(savePort);
return;
}
SetPort(&gInitialState);
SetPortBits(&myBMStep.cellsBM);
{Point hvRatio;
short ratio;
gDisplayRect = defaultRect;
gDisplayRect.left += gMessageZone.h + 1;
gDisplayRect.top += gMessageZone.v + 1;
ratio = GiveRatio(&gInitialState, &gDisplayRect, &hvRatio);
if (ratio == 0)
{
SizeWindow(gLifeW,defaultRect.right - defaultRect.left,
defaultRect.bottom - defaultRect.top, true);
} else
{
SizeWindow(gLifeW,ratio*(gLifeSize.h + 2) + gMessageZone.h + 1,
ratio*(gLifeSize.v + 2) + gMessageZone.v + 1, true);
}
SetPort(gLifeW);
EraseRect(&((GrafPtr)gLifeW)->portRect);
}
iErr = ReadLifeWorld(gLifeFile, &myBMStep, testCurRun, gLifeRun.steps);
if (myBMStep.stepvalue != gLifeRun.computedGen)
{
testCurRun++;
SetPort(savePort);
return;
}
SetPort(&gTempFinalState);
SetPortBits(&myBMStep.cellsBM);
gCurrentState.portBits = gInitialState.portBits;
gCurrentState.portBits.baseAddr =
NewPtr(gCurrentState.portBits.rowBytes *
gCurrentState.portBits.bounds.bottom -
gCurrentState.portBits.bounds.top);
if (gCurrentState.portBits.baseAddr == nil)
{
testCurRun++;
return;
SetPort(savePort);
}
BlockMoveData(gInitialState.portBits.baseAddr,
gCurrentState.portBits.baseAddr,
GetPtrSize(gCurrentState.portBits.baseAddr));
gDisplayRect = ((GrafPtr)gLifeW)->portRect;
gDisplayRect.top += gMessageZone.v + 1;
gDisplayRect.left += gMessageZone.h + 1;
sprintf((char*)gMessage1, (char*)"\pPress Command-T to submit run to your Solution routine.");
sprintf((char*)gMessage2, (char*)"\p");
/*invalidate whole window to force solution display */
SetPort(gLifeW);
InvalRect(&((GrafPtr)gLifeW)->portRect);
testReady = true;
gDisplayState = 1;
SetPort(savePort);
return;
}
}
if (!testSolved)
{
/* Now Test the user solution */
gCurGen = PropagateLife(gCurrentState.portBits, gMaxGens, gBirth, gDeath);
gDisplayState = 2;
// DisplayCells(&gCurrentState, gLifeW, &gDisplayRect);
/* gPreviousState will hold the copy of the solution used for checking */
gErrorState.portBits = gCurrentState.portBits;
gErrorState.portBits.baseAddr = NewPtr(gErrorState.portBits.rowBytes *
gErrorState.portBits.bounds.bottom - gErrorState.portBits.bounds.top);
/* BlockMoveData(gCurrentState.portBits.baseAddr,
gErrorState.portBits.baseAddr, GetPtrSize(gErrorState.portBits.baseAddr));
*/
CopyBits(&gCurrentState.portBits, &gErrorState.portBits,
&gTempFinalState.portBits.bounds, &gErrorState.portBits.bounds,
srcCopy, gErrorState.clipRgn);
CopyBits(&gTempFinalState.portBits, &gErrorState.portBits,
&gTempFinalState.portBits.bounds, &gErrorState.portBits.bounds,
srcXor, gErrorState.clipRgn);
bytesCount = gErrorState.portBits.rowBytes *
(gErrorState.portBits.bounds.bottom -
gErrorState.portBits.bounds.top);
good = true;
for (checkPtr = (long*)gErrorState.portBits.baseAddr;
bytesCount > 0; bytesCount -= 4)
{
if (*checkPtr++)
{
good = false;
goto message;
}
}
message:
if (good)
{
sprintf((char*)gMessage1, (char*)"\pSolution is correct. Press Command-T to continue");
sprintf((char*)gMessage2, (char*)"\p");
} else
{
sprintf((char*)gMessage1, (char*)"\pSolution is INCORRECT. Press Command-T to continue");
sprintf((char*)gMessage2, (char*)"\por use the different menus to display some related bitmaps");
}
SetPort(gLifeW);
messageRect = ((GrafPtr)gLifeW)->portRect;
/*invalidate whole window to force solution display */
InvalRect(&messageRect);
testSolved = true;
testCurRun++;
}
if (testSolved && testReady)
{ testSolved = testReady = false;}
SetPort(savePort);
}
void UpdateLifeWindow(void)
{
Rect messageRect;
GrafPtr savePort;
switch (gDisplayState)
{
case 1:
DisplayCells(&gInitialState, gLifeW, &gDisplayRect);
break;
case 2:
DisplayCells(&gCurrentState, gLifeW, &gDisplayRect);
break;
case 3:
DisplayCells(&gPreviousState, gLifeW, &gDisplayRect);
break;
case 4:
DisplayCells(&gTempFinalState, gLifeW, &gDisplayRect);
break;
case 5:
DisplayCells(&gErrorState, gLifeW, &gDisplayRect);
break;
default:
DisplayCells(&gInitialState, gLifeW, &gDisplayRect);/*will erase all*/
break;
}
messageRect = ((GrafPtr)gLifeW)->portRect;
messageRect.bottom = gMessageZone.v;
GetPort(&savePort);
SetPort(gLifeW);
EraseRect(&messageRect);
MoveTo(15, 20);
DrawString(gMessage1);
MoveTo(15, 40);
DrawString(gMessage2);
SetPort(savePort);
}
void DoNothing()
{}
void DoAboutBox(void)
{
short itemHit;
DialogPtr myDialog;
GrafPtr savePort;
myDialog = GetNewDialog(131, nil, (WindowPtr)-1);
if (myDialog == nil)
return;
GetPort(&savePort);
SetPort(myDialog);
ModalDialog(nil, &itemHit);
DisposeDialog(myDialog);
SetPort(savePort);
}
void PrepareMenus(void)
{
if ((gLifeW != nil) && (gInitialState.portBits.baseAddr != nil))
{
EnableItem(GetMenuHandle(kFileMenu), kSave);
EnableItem(GetMenuHandle(kFileMenu), kAppend);
EnableItem(GetMenuHandle(kLifeMenu), kReturnToInitial);
EnableItem(GetMenuHandle(kLifeMenu), kStartFromCurrent);
EnableItem(GetMenuHandle(kTestMenu), kDisplayInitial);
EnableItem(GetMenuHandle(kTestMenu), kDisplayPrevious);
EnableItem(GetMenuHandle(kTestMenu), kDisplayCurrent);
} else
{
DisableItem(GetMenuHandle(kFileMenu), kSave);
DisableItem(GetMenuHandle(kFileMenu), kAppend);
DisableItem(GetMenuHandle(kLifeMenu), kReturnToInitial);
DisableItem(GetMenuHandle(kLifeMenu), kStartFromCurrent);
DisableItem(GetMenuHandle(kTestMenu), kDisplayInitial);
DisableItem(GetMenuHandle(kTestMenu), kDisplayPrevious);
DisableItem(GetMenuHandle(kTestMenu), kDisplayCurrent);
}
if (gLifeW != nil)
EnableItem(GetMenuHandle(kFileMenu), kClose);
else
DisableItem(GetMenuHandle(kFileMenu), kClose);
if (gInTest)
{
EnableItem(GetMenuHandle(kTestMenu), kDisplayGood);
EnableItem(GetMenuHandle(kTestMenu), kDisplayErrors);
} else
{
DisableItem(GetMenuHandle(kTestMenu), kDisplayGood);
DisableItem(GetMenuHandle(kTestMenu), kDisplayErrors);
}
}
void DoMenu(long menusel)
{
short menuNumber, itemNumber, daRefNum;
Str255 daName;
GrafPtr savePort;
itemNumber = LoWord(menusel);
menuNumber = HiWord(menusel);
switch(menuNumber)
{
case kAppleMenu:
switch (itemNumber)
{
case 1:
DoAboutBox();
break;
default:
GetPort(&savePort);
GetMenuItemText(GetMenuHandle(kAppleMenu), itemNumber, daName);
daRefNum = OpenDeskAcc(daName);
SetPort(savePort);
break;
}
break;
case kFileMenu:
switch (itemNumber)
{
case kNew:
NewRun();
break;
case kOpen:
OpenFile();
break;
case kClose:
CloseFile();
break;
case kSave:
DoSave();
break;
case kSaveAs:
DoNothing();
case kAppend:
AppendtoFile();
break;
case kQuit:
gQuit = true;
break;
default:
break;
}
break;
case kLifeMenu:
switch (itemNumber)
{
case kChangeSettings:
ChangeSettings();
break;
case kPropagate:
PropagateCurrent();
break;
case kPropagateOneStep:
PropagateCurrentbyOne();
break;
case kReturnToInitial:
ReturnToInitial();
break;
case kStartFromCurrent:
StartFromCurrent();
break;
default:
break;
}
break;
case kTestMenu:
switch (itemNumber)
{
case kTestWhole:
case kTestContinue:
TestWholeFile();
break;
case kDisplayInitial:
gDisplayState = 1;
UpdateLifeWindow();
break;
case kDisplayPrevious:
if (gPreviousState.portBits.baseAddr != nil)
{
gDisplayState = 3;
UpdateLifeWindow();
}
break;
case kDisplayCurrent:
if (gCurrentState.portBits.baseAddr != nil)
{
gDisplayState = 2;
UpdateLifeWindow();
}
break;
case kDisplayGood:
if (gTempFinalState.portBits.baseAddr != nil)
{
gDisplayState = 4;
UpdateLifeWindow();
}
break;
case kDisplayErrors:
if (gErrorState.portBits.baseAddr != nil)
{
gDisplayState = 5;
UpdateLifeWindow();
}
break;
default:
break;
}
default :
break;
}
HiliteMenu(0);
}
void DoKey(EventRecord *event)
{ char c;
c = (char)event->message & charCodeMask;
if((event->modifiers & cmdKey) == false)
{
} else
{
PrepareMenus();
DoMenu(MenuKey(c));
}
}
void DoMouseDown(EventRecord *event)
{
WindowPtr theWindow;
Point clickP;
Rect cellRect;
RgnHandle cellRgn;
short ratio;
EventRecord mouseEv;
GrafPtr savePort;
switch (FindWindow(event->where, &theWindow))
{
case inDesk:
break; //out of the application
case inMenuBar:
PrepareMenus();
DoMenu(MenuSelect(event->where));
break;
case inContent:
if ((theWindow == gLifeW) && (gDisplayState == 1))
{
GetPort(&savePort);
SetPort(gLifeW);
cellRgn = NewRgn();
clickP = event->where;
GlobalToLocal(&clickP);
ClickSet(&gInitialState, gLifeW, &gDisplayRect, &clickP, &ratio);
LocalToGlobal(&clickP);
while (Button())
{
SetRect(&cellRect, clickP.h -1, clickP.v -1,
clickP.h + ratio, clickP.v + ratio);
RectRgn(cellRgn, &cellRect);
if (WaitNextEvent(osMask, &mouseEv, 10, cellRgn))
{
clickP = mouseEv.where;
GlobalToLocal(&clickP);
ClickSet(&gInitialState, gLifeW, &gDisplayRect,
&clickP, &ratio);
LocalToGlobal(&clickP);
}
}
DisposeRgn(cellRgn);
SetPort(savePort);
gInitialSaved = gCurrentSaved = false;
if (gInTest)
{
sprintf((char*)gMessage1, (char*)"\p");
sprintf((char*)gMessage2, (char*)"\p");
gInTest = false;
}
}
break;
case inSysWindow:
SystemClick(event,theWindow);
break;
default:
break;
}
}
void main (void)
{
EventRecord theEvent;
InitAll();
#ifdef _profile_
if (!ProfilerInit(collectDetailed, bestTimeBase, 30, 20))
{
#endif
while (!gQuit)
{
if (WaitNextEvent(everyEvent, &theEvent, 10, nil))
{
switch (theEvent.what)
{
case mouseDown:
DoMouseDown(&theEvent);
break;
case keyDown: case autoKey:
DoKey(&theEvent);
break;
case updateEvt:
BeginUpdate((WindowPtr)theEvent.message);
if ((WindowPtr)theEvent.message == gLifeW)
{
UpdateLifeWindow();
}
EndUpdate((WindowPtr)theEvent.message);
break;
case osEvt:
theEvent.when++;
break;
default:
break;
}
}
}
#ifdef _profile_
ProfilerDump("\pLifeDump");
ProfilerTerm();
} else
SysBeep(5);
#endif
}